/**
 * @author Jake.Wang@bstek.com
 */

/**
 * Compute the intersecting point at which a line from a point outside of a
 * rectangle to the middle point of the rectangle meets the rectangle
 */

//获取contextpath  
function _retriveContextPath() {
  var path=window.$setting["common.contextPath"];
  return path;
}

Raphael.fn.getIntersectionPoint = function(fromPoint, to) {
	var toBBox = to.getBBox(), x1 = toBBox.x, y1 = toBBox.y, x2 = fromPoint.x, y2 = fromPoint.y, centerX = x1 + toBBox.width / 2, centerY = y1 + toBBox.height / 2, dx = Math.abs(x1 - x2), dy = Math
			.abs(y1 - y2), tanDYX = dy / dx, tanNode = toBBox.height / toBBox.width, returnPoint = null;

	if (y1 == y2 && x1 < x2) {
		returnPoint = {
			x : x1 + toBBox.width,
			y : centerY
		};
	} else if (y1 == y2 && x1 > x2) {
		returnPoint = {
			x : x1,
			y : centerY
		};
	} else if (x1 == x2 && y1 < y2) {
		returnPoint = {
			x : centerX,
			y : y1 + toBBox.height
		};
	} else if (x1 == x2 && y1 > y2) {
		returnPoint = {
			x : centerX,
			y : y1
		};
	}
	if (x1 > x2 && y1 < y2) {
		if (tanNode >= tanDYX) {
			returnPoint = {
				x : x1,
				y : centerY + tanDYX * toBBox.width / 2
			};
		} else {
			returnPoint = {
				x : centerX - dx / dy * toBBox.height / 2,
				y : y1 + toBBox.height
			};
		}
	} else if (x1 < x2 && y1 < y2) {
		if (tanNode >= tanDYX) {
			returnPoint = {
				x : x1 + toBBox.width,
				y : centerY + tanDYX * toBBox.width / 2
			};
		} else {
			returnPoint = {
				x : centerX + dx / dy * toBBox.height / 2,
				y : y1 + toBBox.height
			};
		}

	} else if (x1 < x2 && y1 > y2) {
		if (tanNode >= tanDYX) {
			returnPoint = {
				x : x1 + toBBox.width,
				y : centerY - tanDYX * toBBox.width / 2
			};
		} else {
			returnPoint = {
				x : centerX + toBBox.height / 2 * dx / dy,
				y : y1
			};
		}
	} else if (x1 > x2 && y1 > y2) {
		if (tanNode >= tanDYX) {
			returnPoint = {
				x : x1,
				y : centerY - toBBox.width / 2 * tanDYX
			};
		} else {
			returnPoint = {
				x : centerX - toBBox.height / 2 * dx / dy,
				y : y1
			};
		}
	}
	return returnPoint;
};

/**
 * Compute the half way point of a line or a fold line
 */
Raphael.fn.getHalfWayPointOfLine = function(from, intermediatePoints, to) {
	// Initialize variables
	intermediatePoints = intermediatePoints || [];
	var pointCount = intermediatePoints.length, fromPoint = from, toPoint = pointCount > 0 ? intermediatePoints[0] : to, length = 0, segmentLength = 0, halfLength = 0, segments = [];

	// Compute the total length
	for ( var i = 0; i < pointCount; i++) {
		toPoint = intermediatePoints[i];

		segmentLength = Raphael.fn.getLineLength(fromPoint, toPoint);
		length += segmentLength;
		segments.push({
			segmentLength : segmentLength,
			fromPoint : fromPoint,
			toPoint : toPoint
		});

		segmentLength = 0;
		fromPoint = toPoint;
		if (pointCount - i == 1) {
			toPoint = to;
		}
	}
	segmentLength = Raphael.fn.getLineLength(fromPoint, toPoint);
	length += segmentLength;
	segments.push({
		segmentLength : segmentLength,
		fromPoint : fromPoint,
		toPoint : toPoint
	});

	// Compute the half length
	halfLength = length / 2;

	// Compute the half way point
	var halfWayPoint = null;
	if (pointCount == 0) {
		halfWayPoint = {
			x : Number(from.x) + (to.x - from.x) / 2,
			y : Number(from.y) + (to.y - from.y) / 2
		};
	} else {
		var lastLength = tempLength = 0;
		for ( var i = 0; i < pointCount + 1; i++) {
			tempLength += Number(segments[i].segmentLength);
			if (tempLength == halfLength) {
				halfWayPoint = segments[i].toPoint;
				break;
			} else if (tempLength > halfLength) {
				var delta = halfLength - lastLength;
				fromPoint = segments[i].fromPoint;
				toPoint = segments[i].toPoint;
				if (fromPoint.x == toPoint.x) {
					if (Number(fromPoint.y) < Number(toPoint.y)) {
						halfWayPoint = {
							x : fromPoint.x,
							y : fromPoint.y + delta
						};
					} else {
						halfWayPoint = {
							x : fromPoint.x,
							y : fromPoint.y - delta
						};
					}
				} else {
					var dx = ((Number(toPoint.x) - Number(fromPoint.x)) / Number(segments[i].segmentLength)) * delta;
					var dy = ((Number(toPoint.y) - Number(fromPoint.y)) / Number(segments[i].segmentLength)) * delta;
					halfWayPoint = {
						x : fromPoint.x + dx,
						y : fromPoint.y + dy
					};
				}
				break;
			}
			lastLength = tempLength;
		}
	}
	return halfWayPoint;
};

// Compute the line length between fromPoint and toPoint
Raphael.fn.getLineLength = function(fromPoint, toPoint) {
	var dx = Math.abs(toPoint.x - fromPoint.x), dy = Math.abs(toPoint.y - fromPoint.y);
	return Math.sqrt(dx * dx + dy * dy);
};

(function(glob) {
	glob.getRequestUrl || (glob.getRequestUrl = function() {
		return _retriveContextPath() + "dorado/uflo/show.process.diagram" + location.search;
	});

	function isChinese(str) {
		return /^([\u4E00-\u9FA5]|[\uFE30-\uFFA0])*$/gi.test(str) ? true : false;
	}

	var jsonWorkflow = paper = createdNodes = null;

	/**
	 * Creates a OnlineWorkflowViewer object with which to draw workflow.
	 */
	function Workflow(canvas, jsonData) {
		if (!jsonData) {
			alert("请指定流程数据！");
			return;
		}

		if (!canvas) {
			alert("请指定canvas!");
			return;
		}
		jsonWorkflow = jsonData;
		var width = jsonWorkflow.width || 800, height = jsonWorkflow.height || 800;
		$("#" + canvas).css({
			"border-width" : jsonWorkflow.borderWidth || 0,
			"border-color" : (jsonWorkflow.borderColor && ("rgb(" + jsonWorkflow.borderColor + ")")) || '',
			"width" : width,
			"height" : height
		});
		paper = Raphael(canvas, width, height);
		createdNodes = {};

	}
	;

	/**
	 * OnlineWorkflowViewer.draw()
	 * 
	 * Draw workflow
	 */
	Workflow.prototype.draw = function() {
		_drawNode();
		_drawConnection();
	};

	/**
	 * Draw workflow node
	 * 
	 */
	function _drawNode() {
		if (jsonWorkflow && jsonWorkflow.nodeDiagrams) {
			var chineseChar = paper.text(0, 0, "中"), englishChar = paper.text(0, 0, "E"), chineseCharBBox = chineseChar.getBBox(), englishCharBBox = englishChar.getBBox(),

			chineseCharWidth = chineseCharBBox.width, chineseCharHeight = chineseCharBBox.height, englishCharWidth = englishCharBBox.width, englishCharHeight = englishCharBBox.height;

			for ( var i = 0; i < jsonWorkflow.nodeDiagrams.length; i++) {
				var node = jsonWorkflow.nodeDiagrams[i];
				if (node.x != null && node.y != null && node.x != undefined && node.y != undefined) {
					
					node.name = node.name || "";
					
					var nodeDeltaX = node.width / 2,
						nodeDeltaY = 0, 
						iconDeltaX = iconDeltaY = textDeltaX = textDeltaY = 0,
						iconWidth = iconHeight = 32,
						info = node.info, 
						borderWidth = node.borderWidth || 1,
						isCircle = (node.shapeType == "Circle"),
					
						rect = paper.rect(node.x, node.y, node.width, node.height, 7).attr({
									"fill" : (node.backgroundColor && ("rgb(" + node.backgroundColor + ")")) || "",
									"fill-opacity" : isCircle ? 0 : (node.backgroundColor) ? 100 : 0,
									"stroke" : isCircle ? '' : (node.borderColor && ("rgb(" + node.borderColor + ")")) || "blue",
									"stroke-width" : borderWidth
								}),

						displayNodeNameInfo = isCircle ? _calculateNodeDisplayName(Number(node.width), node.name, chineseCharWidth, englishCharWidth) : _calculateNodeDisplayName(Number(node.width)
							- borderWidth * 2 - iconWidth - 2, node.name, chineseCharWidth, englishCharWidth),
						nodeContentWidth = iconWidth + 2 + Number(displayNodeNameInfo.displayWidth),

						fontHeight = isChinese(node.name) ? chineseCharHeight : englishCharHeight,

						iconX = (node.icon && (isCircle ? node.x + (node.width - iconWidth) / 2 : node.x + (node.width - nodeContentWidth) / 2)) || 0,
						iconY = (node.icon && (isCircle ? node.y + (node.height - iconHeight - fontHeight - 5) / 2 : node.y + (node.height - iconHeight) / 2)) || 0,
						icon = node.icon && paper.image("dorado/res/" + node.icon, iconX, iconY, iconWidth,iconHeight),
					
						fontColor = (node.fontColor && ("rgb(" + node.fontColor + ")")) || '',
						text = isCircle ? paper.text(node.x + (node.width / 2), node.y + (node.height + iconHeight + 5) / 2, displayNodeNameInfo.displayName).attr({
									"fill" : fontColor
								}) : paper.text(node.x + (node.width - nodeContentWidth) / 2 + iconWidth + 2, node.y + node.height / 2, displayNodeNameInfo.displayName).attr({
									"fill" : fontColor,
									"text-anchor" : "start"
								});
					
					if (node.fontBold) {
						var textBBox = text.getBBox();
						paper.path("M" + textBBox.x + " " + (textBBox.y + textBBox.height + 1) + "L" + (textBBox.x + textBBox.width) + " " + (textBBox.y + textBBox.height + 1)).attr({
								"stroke" : fontColor
							});
					}

					node.time && paper.text(node.x + node.width, node.y - 6, node.time).attr({
							"fill" : "rgb(100,100,100)",
							"font-weight" : "bold",
							"font-size" : 15
						});

					var textBBox = text.getBBox();
					textDeltaX = node.x + node.width/2 -(textBBox.x+textBBox.width/2);
					textDeltaY = node.y - textBBox.y;
					iconDeltaX = node.x + node.width/2 - iconX;
					iconDeltaY = node.y - iconY;
					
					var group = paper.set();
					group.push(rect);
					icon && group.push(icon);
					group.push(text);

					info && group.forEach(function(obj) {
						var deltaX = nodeDeltaX, deltaY = nodeDeltaY;
						if (obj.type == "image") {
							deltaX = iconDeltaX;
							deltaY = iconDeltaY;

						}else if(obj.type =="text"){
							deltaX = textDeltaX;
							deltaY = textDeltaY;
						}
						$(obj.node).qtip({
							content : info,
							position : {
								corner : {
									target : 'topMiddle',
									tooltip : 'bottomMiddle'
								},
								adjust : {
									x : deltaX,
									y : deltaY,
									screen : true
								}
							},
							style : {
								name : 'cream',
								padding : '7px 13px',
								color: "black",
								width : {
									max : 1000,
									min : 0
								},
								tip : true,
								border : {
									width: 0,
									radius:2
								}
							},
							show : 'mouseover',
							hide : 'mouseout'
						});
					});

					// set event listeners
					if (node.events) {
						node.events.click && group.click(node.events.click);
						if (node.events.rclick) {
							group.data("rclick", node.events.rclick);
							group.mousedown(function(e) {
								e = e || window.event;
								(e.which ? (e.which == 3) : (e.button ? (e.button == 2) : false)) && (showDialog());
							});
						}
					}

					if (node.name) {
						createdNodes[node.name] = rect;
					}
				}
			}
			chineseChar.remove();
			englishChar.remove();
		}
	}

	/**
	 * Calculate node display name info including display node name and its
	 * width
	 * 
	 * @param nameWidthLimit
	 *            {Number} the maximum of node-display-name width
	 * @param name
	 *            {String} the original node name
	 * @param chineseCharWidth
	 *            {Number} the width of one Chinese characer width
	 * @param englishCharWidth
	 *            {Number} the width of one English character width
	 * @returns {___anonymous6872_6941} (JSON)
	 */
	function _calculateNodeDisplayName(nameWidthLimit, name, chineseCharWidth, englishCharWidth) {
		name = name || '';
		var displayName = '', nameWidth = 0, ellipsisWidth = 3 * englishCharWidth;
		for ( var i = 0; i < name.length; i++) {
			var tempWidth = nameWidth;
			tempWidth += (isChinese(name.charAt(i)) ? chineseCharWidth : englishCharWidth);
			if (tempWidth < nameWidthLimit || (i == name.length - 1 && tempWidth == nameWidthLimit)) {
				nameWidth = tempWidth;
				displayName += name.charAt(i);
			} else {
				if (nameWidthLimit - nameWidth >= ellipsisWidth) {
					displayName += "...";
					nameWidth += ellipsisWidth;
				} else {
					for ( var j = displayName.length - 1; j >= 0; j--) {
						tempWidth = nameWidth - (isChinese(displayName.charAt(j)) ? chineseCharWidth : englishCharWidth);
						if (nameWidthLimit - tempWidth >= ellipsisWidth) {
							displayName = displayName.substr(0, j) + "...";
							nameWidth = tempWidth + ellipsisWidth;
							break;
						} else {
							nameWidth = tempWidth;
						}
					}
				}
				break;
			}
		}
		return {
			"displayName" : displayName,
			"displayWidth" : nameWidth
		};
	}

	/**
	 * Draw connections between related diagram nodes
	 * 
	 */
	function _drawConnection() {
		if (jsonWorkflow && jsonWorkflow.nodeDiagrams) {
			for ( var i = 0; i < jsonWorkflow.nodeDiagrams.length; i++) {
				_buildPath(jsonWorkflow.nodeDiagrams[i]);
			}
		}
	}

	/**
	 * Build and draw connection paths according to node sequenceFlowDiagrams
	 * 
	 * @param node
	 */
	function _buildPath(node) {
		var flows = node.sequenceFlowDiagrams;
		if (flows && flows.length > 0) {
			for ( var j = 0; j < flows.length; j++) {
				var flow = flows[j], from = _getNode(node.name), to = _getNode(flow.to), path = "", fromPoint = toPoint = null, halfWayPoint = null;
				if (flow.points && flow.points.length > 0) {
					for ( var k = 0; k < flow.points.length; k++) {
						var point = flow.points[k];
						if (flow.points.length == 1) {
							fromPoint = paper.getIntersectionPoint(point, from), toPoint = paper.getIntersectionPoint(point, to);
							halfWayPoint = paper.getHalfWayPointOfLine(fromPoint, flow.points, toPoint);
							path = "M," + _join(fromPoint) + ",L," + _join(point) + ",L," + _join(toPoint);
						} else {
							if (k == 0) {
								fromPoint = paper.getIntersectionPoint(point, from);
								path = "M," + _join(fromPoint) + ",L," + _join(point);
							} else if (flow.points.length - k == 1) {
								toPoint = paper.getIntersectionPoint(point, to);
								halfWayPoint = paper.getHalfWayPointOfLine(fromPoint, flow.points, toPoint);
								path += (",L," + _join(point) + ",L," + _join(toPoint));
							} else {
								path += (",L," + _join(point));
							}
						}
					}

				} else {
					var toBBox = to.getBBox(), fromBBox = from.getBBox(), fromPoint = paper.getIntersectionPoint({
						x : toBBox.x + toBBox.width / 2,
						y : toBBox.y + toBBox.height / 2
					}, from), toPoint = paper.getIntersectionPoint({
						x : fromBBox.x + fromBBox.width / 2,
						y : fromBBox.y + fromBBox.height / 2
					}, to);

					path = "M," + _join(fromPoint) + ",L," + _join(toPoint);
					halfWayPoint = {
						"x" : (toPoint.x - fromPoint.x) / 2 + Number(fromPoint.x),
						"y" : (toPoint.y - fromPoint.y) / 2 + Number(fromPoint.y)
					};
				}

				// draw a connection and a label
				paper.path(path).attr({
					"stroke" : (flow.borderColor && ("rgb(" + flow.borderColor + ")")) || '',
					"stroke-width" : flow.borderWidth || 1,
					"arrow-end" : 'block-wide-long'
				});
				flow.name && paper.text(halfWayPoint.x, halfWayPoint.y, flow.name).attr({
					"fill" : (flow.fontColor && ("rgb(" + flow.fontColor + ")")) || ''
				});
			}
		}
	}

	/**
	 * Convert point to string
	 * 
	 * @param point
	 * @returns {String}
	 */
	function _join(point) {
		return point.x + "," + point.y;
	}

	/**
	 * Get node according to name
	 * 
	 * @param name
	 *            {String}
	 * @returns
	 */
	function _getNode(name) {
		var node = null;
		if (createdNodes && name) {
			node = createdNodes[name];
		}
		if (!node) {
			throw new Error("不存在[" + name + "]对应的节点！");
		}
		return node;
	}

	(typeof module != "undefined" && module.exports) ? (module.exports = OnlineWorkflowViewer) : (typeof define != "undefined" ? (define("OnlineWorkflowViewer", [], function() {
		return Workflow;
	})) : (glob.OnlineWorkflowViewer = Workflow));
}(this));

dorado.widget.UfloProcessDiagram = $extend(dorado.widget.Control, {
	$className : "dorado.widget.UfloProcessDiagram",
	ATTRIBUTES : {
		requestParameterType : {
			defaultValue : "processId"
		},
		requestParameterValue : {},
		iframeId:{}
	},
	refreshDom : function(dom) {
		if (!this._iframeId) {
			return;
		}
		if (this.validateRequestParameters()) {
			var url = _retriveContextPath() + "com.bstek.uflo.console.view.online.UfloProcessDiagram.d?" + this._requestParameterType + "=" + this._requestParameterValue;
			this._view.id(this._iframeId).set("path", url);
		}
	},
	validateRequestParameters : function() {
		if (this._requestParameterType && this._requestParameterValue) {
			return true;
		} else if (this._requestParameterType) {
			dorado.MessageBox.alert("请指定[requestParameterValue]的值！");
			return false;
		} else if (this._requestParameterValue) {
			dorado.MessageBox.alert("请指定[requestParameterType]的值！");
			return false;
		}
	},
	show : function() {
		if (this.validateRequestParameters()) {
			if (this._iframeId) {
				dorado.MessageBox.alert("您已经设置了iframeId,不能调用show方法！");
				return;
			}
			var url = _retriveContextPath() + "com.bstek.uflo.console.view.online.UfloProcessDiagram.d?" + this._requestParameterType + "=" + this._requestParameterValue;
			var iframe = new dorado.widget.IFrame({
				path : url
			});
			var config = {
				icon : $url(">dorado/res/dorado/resources/uflo-process-diagram.png"),
				caption : "流程图查看",
				width : this._width || "600",
				height : this._height || "400",
				center : true,
				modal : true,
				maximizeable : true,
				contentOverflow : "auto",
				children : [ iframe ]
			};
			var dialog = new dorado.widget.Dialog(config);
			dialog.show();
		}
	}
});
